package org.example.expansionpoint.smartInstantiationAwareBeanPostProcessor;

import lombok.Data;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;

/**
 * @Description:
 */

@SpringBootApplication
public class TestSmartInstantiationAwareBeanPostProcessor {
    private static final String STUDENT_BEAN_NAME = "test-student";

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(TestSmartInstantiationAwareBeanPostProcessor.class);
        Teacher teacher = context.getBean(Teacher.class);
        System.out.println(teacher.getClass().getName());
        Student student = context.getBean(Student.class);
        System.out.println(student.getClass().getName());
        context.close();
    }

    @Component(value = STUDENT_BEAN_NAME)
    @Data
    static class Student {
        private String name = "myStudentName";
        private Teacher teacher;

        public Student() {
            System.out.println("Student 无参构造执行");
        }

        public Student(String name) {
            this.name = name;
            System.out.println("Student 有参构造(name)执行");
        }

        public Student(Teacher teacher) {
            this.teacher = teacher;
            System.out.println("Student 有参构造(teacher)执行");
        }

        public Student(String name, Teacher teacher) {
            this.name = name;
            this.teacher = teacher;
            System.out.println("Student 有参构造(name, teacher)执行");
        }

        public void setTeacher(Teacher teacher) {
            this.teacher = teacher;
            System.out.println("Student setTeacher()被调用");
        }
    }

    @Component
    @Data
    static class Teacher {
        private String name = "myTeacherName";

        @Autowired
        private Student student;

        public Teacher() {
            System.out.println("Teacher 无参构造执行");
        }

        public void setStudent(Student student) {
            this.student = student;
            System.out.println("Teacher setStudent()被调用");
        }
    }

    @Component
    static class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
        /**
         * 预测从该处理器的 postProcessBeforeInstantiation() 回调中最终返回的 Bean 类型
         * 默认返回 null。具体实现应尽量预测已知、已缓存的 Bean 类型，而无需额外的处理步骤。
         *
         * @return Bean 的类型，如果无法预测就返回 null
         */
        @Override
        public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
            if (STUDENT_BEAN_NAME.equals(beanName)) {
                System.out.println("Student predictBeanType 方法被执行");
                return Student.class;
            }
            return SmartInstantiationAwareBeanPostProcessor.super.predictBeanType(beanClass, beanName);
        }

        /**
         * 确定从该处理器的 postProcessBeforeInstantiation() 回调中最终返回的 Bean 类型
         * 默认实现会按原样返回给定的 Bean 类。具体实现应全面评估其处理步骤，以便预先创建、初
         * 始化潜在的代理类。
         *
         * @return Bean 的类型，永不为 null
         * @since 6.0
         */
        @Override
        public Class<?> determineBeanType(Class<?> beanClass, String beanName) throws BeansException {
            if (STUDENT_BEAN_NAME.equals(beanName)) {
                System.out.println("Student determineBeanType 方法被执行");
            }
            return SmartInstantiationAwareBeanPostProcessor.super.determineBeanType(beanClass, beanName);
        }

        /**
         * 确定创建给定 Bean 时使用的候选构造方法，默认实现返回 null。
         *
         * @return 候选的构造器，如果没特殊指定，则返回 null，采用默认构造器
         */
        @Override
        public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
            if (STUDENT_BEAN_NAME.equals(beanName)) {
                // 四个构造方法，选默认构造方法
                // 选(name)时会报错，选(teacher)时会有循环依赖，并且是 Spring 无法解决的循环依赖
                // 选(name, teacher)时也会报错
                System.out.println("Student determineCandidateConstructors 方法被执行");
                return new Constructor[]{beanClass.getConstructors()[2]};
            }
            return SmartInstantiationAwareBeanPostProcessor.super.determineCandidateConstructors(beanClass, beanName);
        }

        /**
         * 返回一个引用，以便尽早访问指定的 Bean，这个方法通常用于解决循环引用。
         * 此回调为后置处理器提供了提前暴露 wrapper 的机会，即在目标 Bean 实例完全初始化之前。
         * 暴露的对象应等同于 postProcessBeforeInitialization() 或 postProcessAfterInitialization()
         * 所暴露的对象。请注意，除非后置处理器从上述回调中返回不同的 wrapper，否则
         * 此方法返回的对象将用作 Bean 引用。
         * 默认实现是按原样返回给定的 Bean。
         */
        @Override
        public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
            if (STUDENT_BEAN_NAME.equals(beanName)) {
                System.out.println("Student getEarlyBeanReference 方法被执行");
            }
            return SmartInstantiationAwareBeanPostProcessor.super.getEarlyBeanReference(bean, beanName);
        }

        /**
         * 实例化前执行
         */
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (STUDENT_BEAN_NAME.equals(beanName)) {
                System.out.println("Student postProcessBeforeInstantiation 方法被执行");
            }
            return SmartInstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
        }
    }
}
